\NeedsTeXFormat{LaTeX2e}
\ProvidesPackage{exam}[2021/12/13 v0.1 test]

\newif\ifprint
\printfalse

\DeclareOption{print}{%
  \printtrue
}

\DeclareOption{showframe}{%
  \RequirePackage{showframe}
}

\ProcessOptions

\IfFontExistsTF{Source Han Serif SC}{
    \setCJKmainfont{Source Han Serif SC}
}{}

% \newfontfamily\cmu{CMU Serif}

% \RequirePackage{showframe}

% \ctexset{
%     section/format  =   {\normalsize\raggedright\bfseries}
% }

\RequirePackage{fontawesome5} % 提供大拇指
\RequirePackage{tasks}
\NewTasksEnvironment[label={\faIcon{thumbs-up}}, item-indent=3.5em, label-offset=.5em]{THANKS}[*](4)

\xeCJKsetup{CheckSingle=true}
\RequirePackage{tcolorbox}
\tcbuselibrary{breakable}
\tcbuselibrary{skins}
\tcbset{
    width   =   \linewidth,
    fonttitle=  \bfseries,
    breakable,
    enhanced jigsaw
    % lines before break=0
}
\RequirePackage{mathtools}
\allowdisplaybreaks[4]
\RequirePackage{unicode-math}
\RequirePackage[mono=false]{libertinus-otf}
% \RequirePackage{fourier-otf}
% \setmainfont{TeX Gyre Pagella}
% \setmathfont{TeX Gyre Pagella Math}
% \setmathfont{NewCMMath-Book.otf}
\setmathfont{latinmodern-math.otf}[range={\lbrace, \rbrace}]
% \setmathfont{TeX Gyre Pagella Math}[range=bb]


\RequirePackage[margin=1.5cm]{geometry}

\RequirePackage[notrig]{physics}

\ExplSyntaxOn
\NewDocumentCommand \DV { } { \derivative }
% \let\dv\relax
% 定义 \dx ...
\clist_new:N \l_cf_diff_clist
\clist_set:Nn \l_cf_diff_clist { x, y, z, t, s, u, r, v }
\cs_set_eq:NN \DeclareDocumentCommand:Nnn \DeclareDocumentCommand
\cs_generate_variant:Nn \DeclareDocumentCommand:Nnn { cnn }
\clist_map_inline:Nn \l_cf_diff_clist 
  {
    \DeclareDocumentCommand:cnn { d #1 } { s }
      {
        \IfBooleanTF { ##1 }
        { \dd #1 }
        { \dd{#1} }
      }
  }

\clist_new:N \l_cf_bb_clist
\clist_set:Nn \l_cf_bb_clist { N, Z, Q, J, R, C, K }
\clist_map_inline:Nn \l_cf_bb_clist 
  {
    \DeclareDocumentCommand:cnn { #1 } { } { \symbb{#1} }
  }
\ExplSyntaxOff

\RequirePackage{tabularray}
\UseTblrLibrary{amsmath}

\RequirePackage{nicematrix}
\RequirePackage{hyperref}
\ifprint%
\else%
  \hypersetup{colorlinks=true, linkcolor=blue}
\fi

% https://tex.stackexchange.com/questions/307412/redefine-pi-to-pi-with-unicode-math

\let\set\qty
\let\emph\textbf
\AtBeginDocument{
    \let\ge\geqslant
    \let\le\leqslant
    \let\hom\symscr
    \let\umathpi\pi
    \renewcommand\pi{\symup\umathpi}%
}

\def\grad{\symbf{\nabla}}
\let\boldsymbol\symbfit

\newcommand{\limit}[2]{\lim_{#1 \to #2}}
\newcommand{\me}{\symrm{e}}
\newcommand{\mi}{\symrm{i}}

% 可以用 D(){} 来做定界符
\NewDocumentCommand{\MM}{ O{\R} O{n} }{ \ensuremath{\symup{M}_{#2}(#1)} }
\DeclareMathOperator{\diag}{diag} 
% \DeclareMathOperator{\deg}{deg} 
\DeclareMathOperator{\Image}{Im}
\DeclareMathOperator{\Ker}{Ker}
\DeclareMathOperator{\sgn}{sgn}

% 修改 minipage 的脚注编号为 \arabic
\renewcommand{\thempfootnote}{\arabic{mpfootnote}}

\RequirePackage{amsthm}

\theoremstyle{definition}
\newtheorem*{remark}{注}
\newtheorem{lemma}{引理}
\let\proof\relax
\let\endproof\relax
\newtheorem*{proof}{证明}

% 打印提示和答案列表, 并创建超链接

\newcommand{\printcolor}{\ifprint \else \color{red} \fi}

% 添加红色加粗加星的 \item: \sitem 
% 添加 \textbullet 加星的 \item: \hitem
% https://github.com/jbezos/enumitem/issues/33#issuecomment-993449063 
\def\hitem{%
  \letcs{\originallabel}{labeloriexercise\romannumeral\enit@depth}%
  % the same as \@namedef
  \csedef{labeloriexercise\romannumeral\enit@depth\expandafter}%
    {\noexpand\textbf{\expandonce\textbullet\expandonce\originallabel}}%
  \item
  \cslet{labeloriexercise\romannumeral\enit@depth}\originallabel
}

\def\sitem{%
  \letcs{\originallabel}{labeloriexercise\romannumeral\enit@depth}%
  % the same as \@namedef
  \csedef{labeloriexercise\romannumeral\enit@depth\expandafter}%
    {\noexpand\bfseries\noexpand\printcolor{*\expandonce\originallabel}}%
  \item
  \cslet{labeloriexercise\romannumeral\enit@depth}\originallabel
}

\RequirePackage{verbatim}

% answerfile
\newwrite\ltxex@ansfile
% hintfile
\newwrite\ltxex@hintfile

\def\ltxex@ansfilename{\jobname.ans}
\def\ltxex@hintfilename{\jobname.hint}

% 遇到 \startexercise 时, 打开文件, 并向 .ans 与 .hint 中写入 answersheet 与 hintsheet 的环境头
\newcommand\startexercise{%
  \immediate\openout\ltxex@ansfile=\ltxex@ansfilename
  \immediate\write\ltxex@ansfile{\noexpand\begin{answersheet}}
  \immediate\openout\ltxex@hintfile=\ltxex@hintfilename
  \immediate\write\ltxex@hintfile{\noexpand\begin{hintsheet}}}

% 遇到 \stopexercise 时, 向 .ans 与 .hint 中写入 answersheet 与 hintsheet 的环境尾, 并关闭文件
\newcommand\stopexercise{%
  \immediate\write\ltxex@ansfile{\noexpand\end{answersheet}}%
  \immediate\closeout\ltxex@ansfile
  \immediate\write\ltxex@hintfile{\noexpand\end{hintsheet}}%
  \immediate\closeout\ltxex@hintfile}

% 从 .ans 与 .hint 中输出 answer 和 hint
\newcommand\printanswer{%
  \InputIfFileExists{\ltxex@ansfilename}%
    {\PackageInfo{latexexercise}{answer file `\ltxex@ansfilename' inputed.}}%
    {\PackageWarning{latexexercise}%
      {answer file `\ltxex@ansfilename' does not exsist.}}}

\newcommand\printhint{%
  \InputIfFileExists{\ltxex@hintfilename}%
  {\PackageInfo{latexexercise}{hint file `\ltxex@hintfilename' inputed.}}%
  {\PackageWarning{latexexercise}%
    {hint file `\ltxex@hintfilename' does not exsist.}}}

% 解决对 \item[\ref{}]\label{} 的引用问题
% https://tex.stackexchange.com/a/625985/180617
\NewDocumentCommand{\GetRef}{ m }{\protected@edef\@currentlabel{\getrefnumber{#1}}}%

% 在 answer 环境开始时, 向 .ans 文件中写入 \item[\ref{2:exer}]\label{2:answ}, hint 环境同理
\newenvironment{answer}%
{ 
  \immediate\write\ltxex@ansfile{\noexpand\item[\noexpand\ref{\number\csname c@oriexercisei\endcsname:exer}.] \noexpand\GetRef{\number\csname c@oriexercisei\endcsname:answ}\noexpand\label{\number\csname c@oriexercisei\endcsname:answ}}%
  \ltxex@startwriteans%
}%
{\ltxex@endwrite}

\newenvironment{hint}%
{ 
  \immediate\write\ltxex@hintfile{\noexpand\item[\noexpand\ref{\number\csname c@oriexercisei\endcsname:exer}.] \noexpand\GetRef{\number\csname c@oriexercisei\endcsname:hint}\noexpand\label{\number\csname c@oriexercisei\endcsname:hint}}%
  \ltxex@startwritehint%
}%
{\ltxex@endwrite}

% 定义 oriexercise, answersheet, hintsheet 以及方法 method
\RequirePackage[shortlabels, inline]{enumitem} % 继承并扩展了enumerate宏包的功能
\setlist[enumerate, 1]{leftmargin=*, label=(\roman*)}
% \setlist[enumerate, 2]{leftmargin=*, label=(\arabic*)}
\newlist{oriexercise}{enumerate}{2}
\setlist[oriexercise, 1]{leftmargin=*, label=\arabic*., widest=000, ref=\arabic*}
\setlist[oriexercise, 2]{leftmargin=*, label=(\arabic*)}
\newlist{answersheet}{enumerate}{2}
\setlist[answersheet, 1]{leftmargin=*, label=\arabic*., widest=000, ref=\arabic*}
\setlist[answersheet, 2]{leftmargin=*, label=(\arabic*)}
\newlist{hintsheet}{enumerate}{2}
\setlist[hintsheet, 1]{leftmargin=*, label=\arabic*., widest=000, ref=\arabic*}
\setlist[hintsheet, 2]{leftmargin=*, label=(\arabic*)}
\newlist{method}{enumerate}{1}
\setlist[method, 1]{leftmargin=0pt, labelindent = *, widest={方法二}, label=(\textbf{方法\chinese*})}

\RequirePackage{letltxmacro}
\LetLtxMacro{\it@m}{\item}

\def\asteriskitem{*}
% https://tex.stackexchange.com/a/328393/180617
\NewDocumentCommand{\new@item}{ o }{
    \IfNoValueTF{#1}{\it@m}{\it@m[#1]\phantomsection\protected@edef\@currentlabel{#1}}}

\let\item\new@item

% 在 oriexercise 的基础上定义 exercise 环境, 使得可以输出 [提示][答案], 并带有超链接
\NewDocumentEnvironment{exercise}{ o }{
  \RenewDocumentCommand{\item}{ s s o }{
    \IfNoValueTF{##3}{\it@m}{\it@m[##3]}%
    % 这里注意要判断一下是否在第一层
    \ifnum\enit@depth=\@ne
      \expandafter\label\expandafter{\number\csname c@oriexercisei\endcsname:exer} 
      \ifprint \else
      \ifcsundef{r@\number\csname c@oriexercisei\endcsname:hint}{[暂无]}{
      \hyperref[\number\csname c@oriexercisei\endcsname:hint]{[提示]}}%        
      \ifcsundef{r@\number\csname c@oriexercisei\endcsname:answ}{[暂无]}{
      \hyperref[\number\csname c@oriexercisei\endcsname:answ]{[答案]}}
      \fi
    \fi
  }
  \IfValueTF{#1}{%
    \begin{oriexercise}[#1]
  }{
    \begin{oriexercise}
  }
}{\end{oriexercise}}

% 下面就是我看不懂的文件写入的控制了

\def\ltxex@startwriteans{%
  \begingroup
  \@bsphack
  \let\do\@makeother \dospecials
  \catcode`\^^M\active
  \def\verbatim@processline{%
    \immediate\write\ltxex@ansfile{\the\verbatim@line}}%
  \verbatim@start}

  \def\ltxex@startwritehint{%
  \begingroup
  \@bsphack
  \let\do\@makeother \dospecials
  \catcode`\^^M\active
  \def\verbatim@processline{%
    \immediate\write\ltxex@hintfile{\the\verbatim@line}}%
  \verbatim@start}

\def\ltxex@endwrite{\@esphack\endgroup}
